﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Db4o.Framework.Exceptions;
using Db4o.Framework.Interfaces;

namespace Db4o.Framework
{
    public class ExceptionManager : ILogged
    {
        #region Design pattern : << Singleton ExceptionManager >>

        // read-only private member
        private static readonly ExceptionManager instance = new ExceptionManager();

        // private construtor
        private ExceptionManager() { }

        // public property
        public static ExceptionManager Instance { get { return instance; } }

        #endregion

        #region Warn log methods 
       
        public void ThrowWarnLogException(string exception, string message)
        {
            ThrowWarnLogException(exception, message, null);
        }
        public void ThrowWarnLogException(string exception, string message, Exception inner)
        {
            try
            {
                
                Exception ex = InvokeCoreException(exception, message, inner);
                ex.Source = exception;
                StringBuilder output = new StringBuilder();
                output.AppendFormat("[{3}] {0}: {1}\n{2}\n", ex.Source, ex.Message, ex.StackTrace, DateTime.Now.ToString());
                if (ex.InnerException != null)
                    output.AppendFormat("[{3}] {0}: {1}\n{2}\n", ex.InnerException.Source, ex.InnerException.Message, ex.InnerException.StackTrace, DateTime.Now.ToString());

                logger.WarnFormat(output.ToString());
            }
            catch (Exception e)
            {
                logger.FatalFormat("[{3}] {0}:\n{1}\n{2}\n", e.Source, e.Message, e.StackTrace, DateTime.Now.ToString());
            }
        } 
        #endregion

        #region Error log methods

        public void ThrowErrorLogException(string exception, string message)
        {
            ThrowErrorLogException(exception, message, null);
        }
        public void ThrowErrorLogException(string exception, string message, Exception inner)
        {
            try
            {

                Exception ex = InvokeCoreException(exception, message, inner);
                ex.Source = exception;
                StringBuilder output = new StringBuilder();
                output.AppendFormat("[{3}] {0}: {1}\n{2}\n", ex.Source, ex.Message, ex.StackTrace, DateTime.Now.ToString());
                if (ex.InnerException != null)
                    output.AppendFormat("[{3}] {0}: {1}\n{2}\n", ex.InnerException.Source, ex.InnerException.Message, ex.InnerException.StackTrace, DateTime.Now.ToString());

                logger.ErrorFormat(output.ToString());
            }
            catch (Exception e)
            {
                logger.FatalFormat("[{3}] {0}:\n{1}\n{2}\n", e.Source, e.Message, e.StackTrace, DateTime.Now.ToString());
            }
        }
        #endregion
       
        #region Fatal log methods
        public void ThrowFatalLogException(string exception, string message)
        {
            ThrowFatalLogException(exception, message, null);
        }
        public void ThrowFatalLogException(string exception, string message, Exception inner)
        {
            try
            {
                Exception ex = InvokeCoreException(exception, message, inner);
                ex.Source = exception;

                StringBuilder output = new StringBuilder();
                output.AppendFormat("[{3}] {0}: {1}\n{2}", ex.Source, ex.Message, ex.StackTrace, DateTime.Now.ToString());
                if (ex.InnerException != null)
                    output.AppendFormat("[{3}] {0}: {1}\n{2}", ex.InnerException.Source, ex.InnerException.Message, ex.InnerException.StackTrace, DateTime.Now.ToString());

                logger.FatalFormat(output.ToString());
            }
            catch (Exception e)
            {
                logger.FatalFormat("[{3}] {0}:\n{1}\n{2}\n", e.Source, e.Message, e.StackTrace, DateTime.Now.ToString());
            }
        }
        #endregion

        private Exception InvokeCoreException(string type,string message, Exception inner)
        {
            Assembly asm = Assembly.Load(Assembly.GetCallingAssembly().FullName);
            Type t = asm.GetType("Db4o.Framework.Exceptions."+type);
            // get the specific ctor
            /// TODO: choose the specific using static is not a secure method 
            ConstructorInfo ctor = t.GetConstructors()[2];

            return (Exception)ctor.Invoke(new Object[2] { message, inner });
        }
    }

}
